/*
 ============================================================================
 Name        : Calculator.c
 Author      :Ivan Bannykh, James Soukup
 Version     :
 Copyright   : I made it so its mine and james's no stealing
 Description : Calculator in C, Ansi-style
 ============================================================================
 */
/*standard header files*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <readline/readline.h>
#include <readline/history.h>

/* required for file operations default file names */
#define REG_FILE "myCalcRegisters.txt"
#define REG_FILEH "History.txt"

/*function pointer*/
typedef double MOP_F(double p,double o);

/*Math functions*/
double Subtract(double  p, double o);
double ADD(double  p, double o);
double Divide(double p, double o);
double Multiply(double  p, double o);

/*registry based functions*/
void Write_A_Register(double x, char c, double regArray[26]);
double Read_A_Register(char c, double regArray[26]);
void Save_Registry(double regArray[26]);
void Read_Registry(double regArray[26]);

/*readline histroy based functions*/
void addHistory(char *p);
void readHistory();

/*ascii character checker*/
int ISITALETTER(char * p);







int main()
{
	/*
	 * Max strings is 5
	 * A register with 26 spots for alphabet letters
	 * a counter for the upkept value
	 */
	double RegArray[26]={0};
    double count=0;

    /*defualt for null*/
    char *pEnd;

    char *s1;
    char *s2;
    char *s3;
    char *s4;
    char *s5;
    char *cp;

    /*the argument counter*/

    int ret;

    /*function pointer initialization*/
    MOP_F *op = NULL;

    /*reads the log files we have and brings the values up into ram*/
    Read_Registry(RegArray);
    readHistory();

/*the main body loop*/
  while (1)
  {
	  /*mallocs for out strings*/
	  	  s1=malloc(100*sizeof(char));
	  	  s2=malloc(100*sizeof(char));
	      s3=malloc(100*sizeof(char));
	      s4=malloc(100*sizeof(char));
	      s5=malloc(100*sizeof(char));

	     char *s=malloc(100*sizeof(char));

	     /*our print statment that formats the number*/
	     sprintf(s,"%G: ",count);
	    cp = readline(s);/*one input stream*/

	   /*ctrl D was pressed to exit*/
	    if (cp==NULL)
	     {
	    	printf("\n");
	    	return 0;
	     }

	    /*adds history to current history*/
	     add_history(cp);

	     /*adds history to log file append*/
	     addHistory(cp);

	     /*scans the input string as a sequence of strings*/
	     ret = sscanf(cp, "%s %s %s %s %s",s1,s2,s3,s4,s5);

	     free(cp);


	     /*cases*/
	     /*
	      * Most cases have a general if situation clause so they are all mostly the same
	      * therefore: no comments for all the if statements
	      */
	     switch (ret)
	         {
	         /*
	          * this has cases : -,REG,number
	          */
	         case 1:
	         	/*-*/
	         	if(strncmp (s1,"-",2)==0)
	         	{
	         		count=-count;
	         		break;
	         	}
	         	/*@C*/
	         	else if(strncmp (s1,"@",1)==0)
	         		  {
	         		         	/*takes the character after the @ and its is a Letter*/
	         		      	if(s1[1])
	         		         	{
	         		      			/*writes and saves to registry and the log*/
	         			         		Write_A_Register(count,s1[1],RegArray);
	         			         		Save_Registry(RegArray);
	         			         		break;

	         		         		}
	         		         		break;
	         		         	}
	         	/*X*/
	         	else if(ISITALETTER(s1)==2)
	         	{
	         		/*acc =register*/
	         		count=Read_A_Register(s1[0],RegArray);
	         		break;
	         	}
	         	/* its a number */
	         	else
	         	{
	         		count=strtod(s1,&pEnd);
	         		break;
	         	}
	         	/*options are  or - 5*/
	         case 2:
	        	/*- 5
	        	 * +2
	        	 * + X
	        	 * */
	         	if(strncmp (s1,"-",1)==0)
	         	{
	         		if(ISITALETTER(s2)==2)
	         		{
	         			double add;
	         			add=Read_A_Register(s2[0],RegArray);
	         			op = &Subtract;
	         			count=op(count,add);


	         		}
	         		else if(s2)
	         		{
	         			double y;
	         			y=strtod(s2,&pEnd);
	         			op = &Subtract;
	         			count=op(count,y);
	         			break;

	         		}

	         	}
	         	else if(strncmp (s1,"+",1)==0)
	         	{
	         		if(ISITALETTER(s2)==2)
	         			 	{
	         			double add;
	         				         			add=Read_A_Register(s2[0],RegArray);
	         				         			op = &ADD;
	         				         			count=op(count,add);

	         			     }
	         			    else if(s2)
	         			      {
	         			         			double y;
	         			         			y=strtod(s2,&pEnd);
	         			         			op = &ADD;
	         			         			count=op(count,y);
	         			         			break;

	         			       }
	         	}
	         	else if(strncmp (s1,"/",1)==0)
	         	{
	         		if(ISITALETTER(s2)==2)
	         			         		{
	         			double add;
	         				         			add=Read_A_Register(s2[0],RegArray);
	         				         			op = &Divide;
	         				         			count=op(count,add);

	         			         		}
	         			         		else if(s2)
	         			         		{
	         			         			double y;
	         			         			y=strtod(s2,&pEnd);
	         			         			op = &Divide;
	         			         			count=op(count,y);
	         			         			break;

	         			         		}
	         	}
	         	else if(strncmp (s1,"*",1)==0)
	         		{
	         		if(ISITALETTER(s2)==2)
	         			         		{
	         			double add;
	         				         			add=Read_A_Register(s2[0],RegArray);
	         				         			op = &Multiply;
	         				         			count=op(count,add);

	         			         		}
	         			         		else if(s2)
	         			         		{
	         			         			double y;
	         			         			y=strtod(s2,&pEnd);
	         			         			op = &Multiply;
	         			         			count=op(count,y);
	         			         			break;

	         			         		}
	         		}
	         	else
	         	{
	         	}


	         	/*options are X=5. 5+5*/
	         case 3:
	         	/*starts with a letter x=5 X + X */
	         	if(ISITALETTER(s1)==2)
	         	{
	         		/*x=6*/
	         		if (strncmp (s2,"=",1)==0)
	         		{
	         			double x;
	         			x=strtod(s3,&pEnd);
	         			Write_A_Register(x,s1[0],RegArray);
			         	Save_Registry(RegArray);

	         			break;
	         		}
	         		/*x-*/
	         		if(strncmp (s2,"-",1)==0)
	         			         	{
	         							/*x-x*/
	         			         		if(ISITALETTER(s3)==2)
	         			         		{

	         			         			double add;
	         			         			double x;
	         			         			x=Read_A_Register(s1[0],RegArray);
	         			         			add=Read_A_Register(s3[0],RegArray);
	         			         			op = &Subtract;
	         			         			count=op(x,add);


	         			         		}
	         			         		/*x -3*/
	         			         		else if(s3)
	         			         		{
	         			         			double y;
	         			         			double x;
	         			         			x=Read_A_Register(s1[0],RegArray);
	         			         			y=strtod(s3,&pEnd);
	         			         			op = &Subtract;
	         			         			count=op(x,y);
	         			         			break;

	         			         		}

	         			         	}
	         			         	else if(strncmp (s2,"+",1)==0)
	         			         	{
	         			         		if(ISITALETTER(s3)==2)
	         			         			 	{
	         			         					double x;
	         			         					x=Read_A_Register(s1[0],RegArray);
	         			         					double add;
	         			         				       add=Read_A_Register(s3[0],RegArray);
	         			         				       op = &ADD;
	         			         				        count=op(x,add);

	         			         			     }
	         			         			    else if(s3)
	         			         			      {
	         			         			         			double y;double x;
	         			         			         			x=Read_A_Register(s1[0],RegArray);
	         			         			         			y=strtod(s3,&pEnd);
	         			         			         			op = &ADD;
	         			         			         			count=op(x,y);
	         			         			         			break;

	         			         			       }
	         			         	}
	         			         	else if(strncmp (s2,"/",1)==0)
	         			         	{
	         			         		if(ISITALETTER(s3)==2)
	         			         			         		{
	         			         									double x;
	         			         									x=Read_A_Register(s1[0],RegArray);
	         			         									double add;
	         			         				         			add=Read_A_Register(s3[0],RegArray);
	         			         				         			op = &Divide;
	         			         				         			count=op(x,add);

	         			         			         		}
	         			         			         		else if(s3)
	         			         			         		{
	         			         			         			double y;
	         			         			         			double x;
	         			         			         			x=Read_A_Register(s1[0],RegArray);
	         			         			         			y=strtod(s3,&pEnd);
	         			         			         			op = &Divide;
	         			         			         			count=op(x,y);
	         			         			         			break;

	         			         			         		}
	         			         	}
	         			         	else if(strncmp (s2,"*",1)==0)
	         			         		{
	         			         		if(ISITALETTER(s3)==2)
	         			         			         		{
	         			         								double x;
	         			         								x=Read_A_Register(s1[0],RegArray);
	         			         								double add;
	         			         				         			add=Read_A_Register(s3[0],RegArray);
	         			         				         			op = &Multiply;
	         			         				         			count=op(x,add);

	         			         			         		}
	         			         			         		else if(s3)
	         			         			         		{
	         			         			         			double y;
	         			         			         			double x;
	         			         			         			x=Read_A_Register(s1[0],RegArray);
	         			         			         			y=strtod(s3,&pEnd);
	         			         			         			op = &Multiply;
	         			         			         			count=op(x,y);
	         			         			         			break;

	         			         			         		}
	         			         		}
	         			         	else
	         			         	{
	         			         	}




	         	}
	         	/*5+5*/
	         	else
	         	{
	         		double x;
	         		x=strtod(s1,&pEnd);

	         		if(strncmp (s2,"+",1)==0)
	         		{
	         			if(ISITALETTER(s3)==2)
		         		{
							double add;
			         			add=Read_A_Register(s3[0],RegArray);
			         			op = &ADD;
			         			count=op(x,add);

		         		}
		         		else if(s3)
		         		{
		         			double y;
		         			y=strtod(s3,&pEnd);
		         			op = &ADD;
		         			count=op(x,y);
		         			break;

		         		}


	         		}
	         		if(strncmp (s2,"-",1)==0)
	         		{
	         			if(ISITALETTER(s3)==2)
	         			{
						double add;
		         			add=Read_A_Register(s3[0],RegArray);
		         			op = &Subtract;
		         			count=op(x,add);

	         			}
	         			else if(s3)
	         			{
	         			double y;
	         			y=strtod(s3,&pEnd);
	         			op = &Subtract;
	         			count=op(x,y);
	         			break;

	         			}
	         		}
	         		if(strncmp (s2,"*",1)==0)
	         		{
	         			if(ISITALETTER(s3)==2)
	         			{
						double add;
		         			add=Read_A_Register(s3[0],RegArray);
		         			op = &Multiply;
		         			count=op(x,add);

	         			}
	         		else if(s3)
	         			{
	         			double y;
	         			y=strtod(s3,&pEnd);
	         			op = &Multiply;
	         			count=op(x,y);
	         			break;

	         			}

	         		}
	         		if(strncmp (s2,"/",1)==0)
	         		{
	         			if(ISITALETTER(s3)==2)
	         		{
						double add;
		         			add=Read_A_Register(s3[0],RegArray);
		         			op = &Divide;
		         			count=op(x,add);

	         		}
	         		else if(s3)
	         		{
	         			double y;
	         			y=strtod(s3,&pEnd);
	         			op = &Divide;
	         			count=op(x,y);
	         			break;

	         		}

	         		}

	         	}
	         	/*-5+5*/
	         case 4:
	        	 /*if(s1=="-")*/
	        	if( strncmp (s1,"-",1)==0)
	        	 {
	        		 	 	 	 	 	double x,y;
	        		 	         		x=strtod(s2,&pEnd);
	        		 	         		y=strtod(s4,&pEnd);
	        		 	         		if(strncmp(s3,"+",1)==0)
	        		 	         		{
	        		 	         			op = &ADD;
	        		 	         			count=op(-x,y);
	        		      		    		break;

	        		 	         		}
	        		 	         		if(strncmp (s3,"-",1)==0)
	        		 	         		{
	        		 	         			op = &Subtract;
	        		 	         			count=op(-x,y);
	        		      		    		break;

	        		 	         		}
	        		 	         		if(strncmp (s3,"*",1)==0)
	        		 	         		{
	        		 	         			op = &Multiply;
	        		 	         			count=op(-x,y);
	        		      		    		break;

	        		 	         		}
	        		 	         		if(strncmp (s3,"/",1)==0)
	        		 	         		{
	        		 	         			op = &Divide;
	        		 	         			count=op(-x,y);
	        		      		    		break;

	        		 	         		}

	        	 }

	        	 else
	        	 {

	        	 }
	        	 /*-5+-5*/
	         case 5:
	         	        	 if(strncmp (s1,"-",1)==0 && strncmp (s4,"-",1)==0)
	         	        	 {
	         	        		 	 	 	 	 	double x,y;
	         	        		 	         		x=strtod(s2,&pEnd);
	         	        		 	         		y=strtod(s5,&pEnd);
	         	        		 	         		if(strncmp (s3,"+",1)==0)
	         	        		 	         		{
	         	        		 	         			op = &ADD;
	         	        		 	         			count=op(-x,-y);
	         	        		      		    		break;


	         	        		 	         		}
	         	        		 	         		if(strncmp (s3,"-",1)==0)
	         	        		 	         		{
	         	        		 	         			op = &Subtract;
	         	        		 	         			count=op(-x,-y);
	         	        		      		    		break;

	         	        		 	         		}
	         	        		 	         		if(strncmp (s3,"*",1)==0)
	         	        		 	         		{
	         	        		 	         			op = &Multiply;
	         	        		 	         			count=op(-x,-y);
	         	        		      		    		break;

	         	        		 	         		}
	         	        		 	         		if(strncmp (s3,"/",1)==0)
	         	        		 	         		{
	         	        		 	         			op = &Divide;
	         	        		 	         			count=op(-x,-y);
	         	        		      		    		break;

	         	        		 	         		}

	         	        	 }


	         default:
	         	break;

	         	/*free the mallocs for the pointers*/
	         	free(s1);
	         	free(s2);
	         	free(s3);
	         	free(s4);
	         	free(s5);
	         }


	      /*strtod	 Convert string to double
	       * Cases
	       * @X 1
	       * X 1
	       * - 1
	       * 5 1
	       * -5 1
	       * - 5 2
	       * X=5 3
	       * 5+5 3
	       * -5+5 3
	       * -5+-5 3
	       * X + X
	       * +X
	       * -X
	       */


  }
  return 0;
}

/*the math functions*/
double Subtract(double  p, double o)
{
	double x;
	x=p-o;
	return x;
}
double ADD(double  p, double o)
{
	double x;
	x=p+o;
	return x;
}
double Divide(double  p, double o)
{
	double x;
	x=p/o;
	return x;
}
double Multiply(double  p, double o)
{
	double x;
	x=p*o;
	return x;
}


/*take a double array and convert to a char string seperated by /n
put into a file.txt
read .txt and write to a double array.
*/

void Save_Registry(double regArray[26])
{
	int i, j;
	FILE *fp;
	/* Open the file for writing */
	fp = fopen(REG_FILE, "w");
	/* Iterate through the reg Array and Write to the file using fprintf */
	for( i = 0; i < 26; i++ )
	{
		j = fprintf(fp, "%lf,", regArray[i]);
		/* fprintf returns negative when it doesnt write anything,
		   this is an error */
		if( j < 0 )
		{
			perror("File Write Failure!");
			exit(10);
		}
	}
	/* Close the file */
	fclose(fp);
	return;
}

void Read_Registry(double regArray[26])
{
	int i, j = 0;
	/* long int pos1, pos2; */
	double intP, fracP;
	char  c;
	FILE *fp;
	/* Open the file for reading */
	fp = fopen(REG_FILE, "r");
	if( fp )
	{
		/* Scan the file for doubles */
		for( i = 0; i < 26; i++ )
		{
			/*scans the file for a double and then a char and places thedouble into the array*/
			fscanf(fp, "%lf%c", &regArray[i], &c);

			/* Use mod function to split up the double */
			fracP = modf(regArray[i], &intP);
			/* If the fractional part is 0 then we have an int */

		}
		fclose(fp);
		return;
	}
	else
	{
		/*no previous log file create a new one filled with zeros*/

		fp = fopen(REG_FILE, "w");

		for( i = 0; i < 26; i++ )
		{
			regArray[i] = 0;
			j = fprintf(fp, "%lf,", regArray[i]);
			if( j < 0 )
			{
				perror("File Write Failure!");
				exit(10);
			}
		}
		fclose(fp);
	}
}

/*reads a character  letter and converts it to an appropriate array index and returns the valley*/
double Read_A_Register(char c, double regArray[26])
{
	int asciiVal = c;
	if( asciiVal < 91 )
	{
		return regArray[asciiVal-65];
	}
	else
	{
		return regArray[asciiVal-97];
	}
}

/*takes a letter  and converts it to the array index  and then stores the double into that array*/
void Write_A_Register(double x, char c, double regArray[26])
{
	int asciiVal = c;
	if( asciiVal < 91 )
	{
		regArray[asciiVal-65] = x;
	}
	else
	{
		regArray[asciiVal-97] = x;
	}
	return;
}

/*creates a history log file */
void addHistory(char *p)
{

	/*seperates the characters by spaces so readback is possible*/
	p=strcat(p," ");

	int j;
	FILE *fp;
	/* Open the file for writing using the append so it is allways maintained */
	fp = fopen(REG_FILEH, "a");
	/* Iterate through the reg Array and Write to the file using fprintf */
	j = fprintf(fp, "%s,", p);
		/* fprintf returns negative when it doesnt write anything,
		   this is an error */
		if( j < 0 )
		{
			perror("File Write Failure!");
			exit(10);
		}
		fclose(fp);
}
	/* Close the file */

void readHistory()
{
	FILE *fp;
	char *c = malloc(100*sizeof(char));
	int j;
/* Open the file for writing */

		fp = fopen(REG_FILEH, "r");

		if( fp )
			{
			/*goes through the input stream until the end and adds the strings int history one by one*/
			/*spaces seperate strings from another*/
			while(feof(fp)== 0)
			{

			      fscanf(fp, "%s",c);
			      /*commas are removed by incrementing the pointer since it is like an array*/
			      if (c[0] == ',')
			      {
			    	  c++;
			      }

				add_history(c);

				}
			fclose(fp);
			}

			else
			{
				/*no log file create a new one with a zero*/
					fp = fopen(REG_FILEH, "w");

					j = fprintf(fp, "%c",'0' );
					if( j < 0 )
					{
						perror("File Write Failure!");
						exit(10);
					}
					fclose(fp);

			}

	}

/*checks if the character fits a letter and returns 2 if true*/
int ISITALETTER(char * x)
{

	if(strncmp (x,"q",1)==0 ||strncmp (x,"w",1)==0 ||strncmp (x,"e",1)==0 ||strncmp (x,"r",1)==0 ||
			strncmp (x,"t",1)==0 ||strncmp (x,"y",1)==0 ||strncmp (x,"u",1)==0 ||strncmp (x,"i",1)==0 ||
			strncmp (x,"o",1)==0 ||strncmp (x,"p",1)==0 ||strncmp (x,"a",1)==0 ||strncmp (x,"s",1)==0 ||
			strncmp (x,"d",1)==0 ||strncmp (x,"f",1)==0 ||strncmp (x,"g",1)==0 ||strncmp (x,"h",1)==0 ||
			strncmp (x,"j",1)==0 ||strncmp (x,"k",1)==0 ||strncmp (x,"l",1)==0 ||strncmp (x,"z",1)==0 ||
			strncmp (x,"x",1)==0 ||strncmp (x,"c",1)==0 ||strncmp (x,"v",1)==0 ||strncmp (x,"b",1)==0 ||
			strncmp (x,"n",1)==0 ||strncmp (x,"m",1)==0 ||strncmp (x,"Q",1)==0 ||strncmp (x,"W",1)==0 ||
			strncmp (x,"E",1)==0 ||strncmp (x,"R",1)==0 ||strncmp (x,"T",1)==0 ||strncmp (x,"Y",1)==0 ||
			strncmp (x,"U",1)==0 ||strncmp (x,"I",1)==0 ||strncmp (x,"O",1)==0 ||strncmp (x,"P",1)==0 ||
			strncmp (x,"A",1)==0 ||strncmp (x,"S",1)==0 ||strncmp (x,"D",1)==0 ||strncmp (x,"F",1)==0 ||
			strncmp (x,"G",1)==0 ||strncmp (x,"H",1)==0 ||strncmp (x,"J",1)==0 ||strncmp (x,"K",1)==0 ||
			strncmp (x,"L",1)==0 ||strncmp (x,"Z",1)==0 ||strncmp (x,"X",1)==0 ||strncmp (x,"C",1)==0 ||
			strncmp (x,"V",1)==0 ||strncmp (x,"B",1)==0 ||strncmp (x,"N",1)==0 ||strncmp (x,"M",1)==0)
	{
		return 2;
	}

	else
	{
		return 0;
	}
}

